{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Tutorial III: Low rank, arbitrary basis molecular simulations"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Important note about origins of this method\n",
    "\n",
    "What follows in this tutorial is currently unpublished. It will be described in detail in an upcoming paper by (alphabetical) Ryan Babbush, Garnet Kin-Lic Chan, Jarrod McClean, Austin Minnich, Mario Motta, and Erika Ye."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Low rank decomposition of the Coulomb operator\n",
    "\n",
    "In Tutorial I we discussed methods for very compiling single-particle basis transformations of fermionic operators in $O(N)$ depth on a linearly connected architecture. We looked at the particular example of simulating a free fermion model by using Bogoliubov transformations to diagonalize the model.\n",
    "\n",
    "In Tutorial II we discussed methods for compiling Trotter steps of electronic structure Hamiltonian in $O(N)$ depth on a linearly connected architecture when expressed in a basis diagonalizing the Coulomb operator so that\n",
    "$$\n",
    "H = \\sum_{pq} T_{pq} a^\\dagger_p a_q + \\sum_{pq} V_{pq} a^\\dagger_p a_p a^\\dagger_q a_q.\n",
    "$$\n",
    "\n",
    "Here we will discuss how both of those techniques can be combined, along with some insights from electronic structure, in order to simulate arbitrary basis molecular Hamiltonians taking the form\n",
    "$$\n",
    "H = \\sum_{pq} T_{pq} a^\\dagger_p a_q + \\sum_{pqrs} V_{pqrs} a^\\dagger_p a_q a^\\dagger_r a_s\n",
    "$$\n",
    "in depth scaling only as $O(N^2)$ on a linear array of qubits. First, we note that the one-body part of the above expression is easy to simulate using the techniques introduced in Tutorial I. Thus, the real challenge is to simulate the two-body part of the operator.\n",
    "\n",
    "We begin with the observation that the rank-4 tensor $V$, with the values $V_{pqrs}$ representing the coefficient of $a^\\dagger_p a_q a^\\dagger_r a_s$ can be flattened into an $N^2 \\times N^2$ array by making $p,q$ one index and $r,s$ the other. This is the electronic repulsion integral (ERI) matrix in chemist notation. We will refer to the ERI matrix as $W$. By diagonalizing $W$, one obtains $W g_\\ell = \\lambda_\\ell g_\\ell$ where the eigenvector $g_\\ell$ is a vector of dimension $N^2$. If we reshape $g_\\ell$ into an $N \\times N$ vector, we realize that\n",
    "$$\n",
    "\\sum_{pqrs} V_{pqrs} a^\\dagger_p a_q a^\\dagger_r a_s = \\sum_{\\ell=0}^{L-1} \\lambda_\\ell \\left(\\sum_{pq} \\left[g_{\\ell}\\right]_{pq} a^\\dagger_p a_q\\right)^2.\n",
    "$$\n",
    "This is related to the concept of density fitting in electronic structure, which is often accomplished using a Cholesky decomposition. It is fairly well known in the quantum chemistry community that the ERI matrix is positive semi-definite and despite having linear dimension $N^2$, has rank of only $L = O(N)$. Thus, the eigenvalues $\\lambda_\\ell$ are positive and there are only $O(N)$ of them.\n",
    "\n",
    "Next, we diagonalize the one-body operators inside of the square so that\n",
    "$$\n",
    "R_\\ell \\left(\\sum_{pq} \\left[g_\\ell\\right]_{pq} a^\\dagger_p a_q\\right) R_\\ell^\\dagger = \\sum_{p} f_{\\ell p} a^\\dagger_p a_p\n",
    "$$\n",
    "where the $R_\\ell$ represent single-particle basis transformations of the sort we compiled in Tutorial I. Then,\n",
    "$$\n",
    "\\sum_{\\ell=0}^{L-1} \\lambda_\\ell \\left(\\sum_{pq} \\left[g_{\\ell}\\right]_{pq} a^\\dagger_p a_q\\right)^2 =\n",
    "\\sum_{\\ell=0}^{L-1} \\lambda_\\ell \\left(R_\\ell \\left(\\sum_{p} f_{\\ell p} a^\\dagger_p a_p\\right) R_\\ell^\\dagger\\right)^2 = \\sum_{\\ell=0}^{L-1} \\lambda_\\ell \\left(R_\\ell \\left(\\sum_{p} f_{\\ell p} a^\\dagger_p a_p\\right) R_\\ell^\\dagger R_\\ell \\left(\\sum_{p} f_{\\ell p} a^\\dagger_p a_p\\right) R_\\ell^\\dagger\\right)\n",
    "= \\sum_{\\ell=0}^{L-1} \\lambda_\\ell R_\\ell \\left(\\sum_{pq} f_{\\ell p} f_{\\ell q} a^\\dagger_p a_p a^\\dagger_q a_q\\right) R_\\ell^\\dagger.\n",
    "$$\n",
    "We now see that we can simulate a Trotter step under the arbitrary basis two-body operator as\n",
    "$$\n",
    "\\prod_{\\ell=0}^{L-1} R_\\ell \\exp\\left(-i\\sum_{pq} f_{\\ell p} f_{\\ell q} a^\\dagger_p a_p a^\\dagger_q a_q\\right) R_\\ell^\\dagger\n",
    "$$\n",
    "where we note that the operator in the exponential take the form of a diagonal Coulomb operator. Since we can implement the $R_\\ell$ circuits in $O(N)$ depth (see Tutorial I) and we can implement Trotter steps under diagonal Coulomb operators in $O(N)$ layers of gates (see Tutorial II) we see that we can implement Trotter steps under arbitrary basis electronic structure Hamiltionians in $O(L N) = O(N^2)$ depth, and all on a linearly connected device. This is a big improvement over the usual way of doing things, which would lead to no less than $O(N^5)$ depth! In fact, it is also possible to do better by truncating rank on the second diagonalization but we have not implemented that (details will be discussed in aforementioned paper-in-preparation).\n",
    "\n",
    "Note that these techniques are also applicable to realizing evolution under other two-body operators, such as the generator of unitary coupled cluster. Note that one can create variational algorithms where a variational parameter specifies the rank at which to truncate the $\\lambda_\\ell$."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Example implementation: Trotter steps of LiH in molecular orbital basis\n",
    "\n",
    "We will now use these techniques to implement Trotter steps for an actual molecule. We will focus on LiH at equilibrium geometry, since integrals for that system are provided with every OpenFermion installation. However, by installing [OpenFermion-PySCF](https://github.com/quantumlib/OpenFermion-PySCF) or [OpenFermion-Psi4](https://github.com/quantumlib/OpenFermion-Psi4) one can use these techniques for any molecule at any geometry. We will generate LiH in an active space consisting of 4 qubits. First, we obtain the Hamiltonian as an InteractionOperator."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "-6.7698132180879735 [] +\n",
      "-0.7952726864779313 [0^ 0] +\n",
      "0.24889540266275176 [0^ 0^ 0 0] +\n",
      "-0.02307282640154995 [0^ 0^ 0 2] +\n",
      "-0.023072826401549944 [0^ 0^ 2 0] +\n",
      "0.005865992881900444 [0^ 0^ 2 2] +\n",
      "0.24889540266275176 [0^ 1^ 1 0] +\n",
      "-0.02307282640154995 [0^ 1^ 1 2] +\n",
      "-0.023072826401549944 [0^ 1^ 3 0] +\n",
      "0.005865992881900444 [0^ 1^ 3 2] +\n",
      "0.04614563473199314 [0^ 2] +\n",
      "-0.02307282640154995 [0^ 2^ 0 0] +\n",
      "0.005865992881900456 [0^ 2^ 0 2] +\n",
      "0.11412688446849813 [0^ 2^ 2 0] +\n",
      "0.0027487522157917266 [0^ 2^ 2 2] +\n",
      "-0.02307282640154995 [0^ 3^ 1 0] +\n",
      "0.005865992881900456 [0^ 3^ 1 2] +\n",
      "0.11412688446849813 [0^ 3^ 3 0] +\n",
      "0.0027487522157917266 [0^ 3^ 3 2] +\n",
      "0.24889540266275176 [1^ 0^ 0 1] +\n",
      "-0.02307282640154995 [1^ 0^ 0 3] +\n",
      "-0.023072826401549944 [1^ 0^ 2 1] +\n",
      "0.005865992881900444 [1^ 0^ 2 3] +\n",
      "-0.7952726864779313 [1^ 1] +\n",
      "0.24889540266275176 [1^ 1^ 1 1] +\n",
      "-0.02307282640154995 [1^ 1^ 1 3] +\n",
      "-0.023072826401549944 [1^ 1^ 3 1] +\n",
      "0.005865992881900444 [1^ 1^ 3 3] +\n",
      "-0.02307282640154995 [1^ 2^ 0 1] +\n",
      "0.005865992881900456 [1^ 2^ 0 3] +\n",
      "0.11412688446849813 [1^ 2^ 2 1] +\n",
      "0.0027487522157917266 [1^ 2^ 2 3] +\n",
      "0.04614563473199314 [1^ 3] +\n",
      "-0.02307282640154995 [1^ 3^ 1 1] +\n",
      "0.005865992881900456 [1^ 3^ 1 3] +\n",
      "0.11412688446849813 [1^ 3^ 3 1] +\n",
      "0.0027487522157917266 [1^ 3^ 3 3] +\n",
      "0.04614563473199324 [2^ 0] +\n",
      "-0.023072826401549958 [2^ 0^ 0 0] +\n",
      "0.11412688446849814 [2^ 0^ 0 2] +\n",
      "0.005865992881900458 [2^ 0^ 2 0] +\n",
      "0.002748752215791747 [2^ 0^ 2 2] +\n",
      "-0.023072826401549958 [2^ 1^ 1 0] +\n",
      "0.11412688446849814 [2^ 1^ 1 2] +\n",
      "0.005865992881900458 [2^ 1^ 3 0] +\n",
      "0.002748752215791747 [2^ 1^ 3 2] +\n",
      "-0.36549257026798354 [2^ 2] +\n",
      "0.0058659928819004585 [2^ 2^ 0 0] +\n",
      "0.002748752215791731 [2^ 2^ 0 2] +\n",
      "0.002748752215791736 [2^ 2^ 2 0] +\n",
      "0.16959219087341962 [2^ 2^ 2 2] +\n",
      "0.0058659928819004585 [2^ 3^ 1 0] +\n",
      "0.002748752215791731 [2^ 3^ 1 2] +\n",
      "0.002748752215791736 [2^ 3^ 3 0] +\n",
      "0.16959219087341962 [2^ 3^ 3 2] +\n",
      "-0.023072826401549958 [3^ 0^ 0 1] +\n",
      "0.11412688446849814 [3^ 0^ 0 3] +\n",
      "0.005865992881900458 [3^ 0^ 2 1] +\n",
      "0.002748752215791747 [3^ 0^ 2 3] +\n",
      "0.04614563473199324 [3^ 1] +\n",
      "-0.023072826401549958 [3^ 1^ 1 1] +\n",
      "0.11412688446849814 [3^ 1^ 1 3] +\n",
      "0.005865992881900458 [3^ 1^ 3 1] +\n",
      "0.002748752215791747 [3^ 1^ 3 3] +\n",
      "0.0058659928819004585 [3^ 2^ 0 1] +\n",
      "0.002748752215791731 [3^ 2^ 0 3] +\n",
      "0.002748752215791736 [3^ 2^ 2 1] +\n",
      "0.16959219087341962 [3^ 2^ 2 3] +\n",
      "-0.36549257026798354 [3^ 3] +\n",
      "0.0058659928819004585 [3^ 3^ 1 1] +\n",
      "0.002748752215791731 [3^ 3^ 1 3] +\n",
      "0.002748752215791736 [3^ 3^ 3 1] +\n",
      "0.16959219087341962 [3^ 3^ 3 3]\n"
     ]
    }
   ],
   "source": [
    "import openfermion\n",
    "\n",
    "# Set Hamiltonian parameters for LiH simulation in active space.\n",
    "diatomic_bond_length = 1.45\n",
    "geometry = [('Li', (0., 0., 0.)), ('H', (0., 0., diatomic_bond_length))]\n",
    "basis = 'sto-3g'\n",
    "multiplicity = 1\n",
    "active_space_start = 1\n",
    "active_space_stop = 3\n",
    "\n",
    "# Generate and populate instance of MolecularData.\n",
    "molecule = openfermion.MolecularData(geometry, basis, multiplicity, description=\"1.45\")\n",
    "molecule.load()\n",
    "\n",
    "# Get the Hamiltonian in an active space.\n",
    "molecular_hamiltonian = molecule.get_molecular_hamiltonian(\n",
    "    occupied_indices=range(active_space_start),\n",
    "    active_indices=range(active_space_start, active_space_stop))\n",
    "print(openfermion.get_fermion_operator(molecular_hamiltonian))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We see from the above output that this is a fairly complex Hamiltonian already. Next we will use the `simulate_trotter` function from Tutorial I, but this time using a different type of Trotter step associated with these low rank techniques. To keep this circuit very short for pedagogical purposes we will force a truncation of the eigenvalues $\\lambda_\\ell$ at a predetermined value of `final_rank`. While we also support a canned `LOW_RANK` option for the Trotter steps, in order to pass this value of `final_rank` we will instantiate a custom Trotter algorithm type."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "0           1           2           3\n",
      "│           │           │           │\n",
      "Rz(π)       Rz(π)       Rz(π)       Rz(π)\n",
      "│           │           │           │\n",
      "│           YXXY────────#2^-1       │\n",
      "│           │           │           │\n",
      "YXXY────────#2^0.081    │           │\n",
      "│           │           │           │\n",
      "Rz(0.337π)  │           YXXY────────#2^-0.081\n",
      "│           │           │           │\n",
      "│           YXXY────────#2^-1       │\n",
      "│           │           │           │\n",
      "│           Rz(0.337π)  │           Rz(0.169π)\n",
      "│           │           │           │\n",
      "│           │           Rz(0.169π)  │\n",
      "│           │           │           │\n",
      "│           │           │           │\n",
      "│           │           │           │\n",
      "│           YXXY────────#2^-1       │\n",
      "│           │           │           │\n",
      "YXXY────────#2^-0.051   │           │\n",
      "│           │           │           │\n",
      "│           │           YXXY────────#2^0.051\n",
      "│           │           │           │\n",
      "│           YXXY────────#2^-1       │\n",
      "│           │           │           │\n",
      "@───────────@^-0.145    │           │\n",
      "│           │           │           │\n",
      "×───────────×           @───────────@^(-1/15)\n",
      "│           │           │           │\n",
      "│           │           ×───────────×\n",
      "│           │           │           │\n",
      "│           @───────────@^-0.098    │\n",
      "│           │           │           │\n",
      "│           ×───────────×           │\n",
      "│           │           │           │\n",
      "@───────────@^-0.098    @───────────@^-0.098\n",
      "│           │           │           │\n",
      "×───────────×           ×───────────×\n",
      "│           │           │           │\n",
      "Rz(-0.033π) @───────────@^-0.098    Rz(-0.072π)\n",
      "│           │           │           │\n",
      "│           ×───────────×           │\n",
      "│           │           │           │\n",
      "│           Rz(-0.033π) Rz(-0.072π) │\n",
      "│           │           │           │\n",
      "│           Rz(π)       Rz(π)       │\n",
      "│           │           │           │\n",
      "│           #2──────────YXXY^-1     │\n",
      "│           │           │           │\n",
      "│           │           #2──────────YXXY^-0.95\n",
      "│           │           │           │\n",
      "#2──────────YXXY^0.95   │           │\n",
      "│           │           │           │\n",
      "│           #2──────────YXXY^-1     │\n",
      "│           │           │           │\n",
      "│           │           @───────────@^-0.041\n",
      "│           │           │           │\n",
      "@───────────@^-0.019    ×───────────×\n",
      "│           │           │           │\n",
      "×───────────×           │           │\n",
      "│           │           │           │\n",
      "│           @───────────@^0.028     │\n",
      "│           │           │           │\n",
      "│           ×───────────×           │\n",
      "│           │           │           │\n",
      "@───────────@^0.028     @───────────@^0.028\n",
      "│           │           │           │\n",
      "×───────────×           ×───────────×\n",
      "│           │           │           │\n",
      "Rz(-0.02π)  @───────────@^0.028     Rz(-0.009π)\n",
      "│           │           │           │\n",
      "│           ×───────────×           │\n",
      "│           │           │           │\n",
      "│           Rz(-0.02π)  Rz(-0.009π) │\n",
      "│           │           │           │\n",
      "│           Rz(π)       Rz(π)       │\n",
      "│           │           │           │\n",
      "│           YXXY────────#2^-1       │\n",
      "│           │           │           │\n",
      "YXXY────────#2^-0.918   │           │\n",
      "│           │           │           │\n",
      "│           │           YXXY────────#2^0.918\n",
      "│           │           │           │\n",
      "│           YXXY────────#2^-1       │\n",
      "│           │           │           │\n",
      "│           │           │           │\n",
      "│           │           │           │\n"
     ]
    }
   ],
   "source": [
    "import cirq\n",
    "import openfermioncirq\n",
    "from openfermioncirq import trotter\n",
    "\n",
    "# Trotter step parameters.\n",
    "time = 1.\n",
    "final_rank = 2\n",
    "\n",
    "# Initialize circuit qubits in a line.\n",
    "n_qubits = openfermion.count_qubits(molecular_hamiltonian)\n",
    "qubits = cirq.LineQubit.range(n_qubits)\n",
    "\n",
    "# Compile the low rank Trotter step using OpenFermion-Cirq.\n",
    "custom_algorithm = trotter.LowRankTrotterAlgorithm(final_rank=final_rank)\n",
    "circuit = cirq.Circuit(\n",
    "    trotter.simulate_trotter(\n",
    "            qubits, molecular_hamiltonian,\n",
    "            time=time, omit_final_swaps=True,\n",
    "            algorithm=custom_algorithm),\n",
    "    strategy=cirq.InsertStrategy.EARLIEST)\n",
    "\n",
    "# Print circuit.\n",
    "cirq.DropNegligible().optimize_circuit(circuit)\n",
    "print(circuit.to_text_diagram(transpose=True))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We were able to print out the circuit this way but forcing `final_rank` of 2 is not very accurate. In the cell below, we compile the Trotter step with full rank so $L = N^2$ and depth is actually $O(N^3)$ and repeat the Trotter step multiple times to show that it actually converges to the correct result. Since we are not forcing the rank truncation we can use the built-in `LOW_RANK` Trotter step type. Note that the rank of the Coulomb operators is asymptotically $O(N)$ but for very small molecules in small basis sets only a few eigenvalues can be truncated."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Fidelity with exact result is 0.9999959751965096.\n",
      "\n",
      "0           1           2           3\n",
      "│           │           │           │\n",
      "Rz(π)       Rz(π)       Rz(π)       Rz(π)\n",
      "│           │           │           │\n",
      "│           YXXY────────#2^-1       │\n",
      "│           │           │           │\n",
      "YXXY────────#2^0.081    │           │\n",
      "│           │           │           │\n",
      "Rz(0.112π)  │           YXXY────────#2^-0.081\n",
      "│           │           │           │\n",
      "│           YXXY────────#2^-1       │\n",
      "│           │           │           │\n",
      "│           Rz(0.112π)  │           Rz(0.056π)\n",
      "│           │           │           │\n",
      "│           │           Rz(0.056π)  │\n",
      "│           │           │           │\n",
      "│           │           │           │\n",
      "│           │           │           │\n",
      "│           YXXY────────#2^-1       │\n",
      "│           │           │           │\n",
      "YXXY────────#2^-0.051   │           │\n",
      "│           │           │           │\n",
      "│           │           YXXY────────#2^0.051\n",
      "│           │           │           │\n",
      "│           YXXY────────#2^-1       │\n",
      "│           │           │           │\n",
      "@───────────@^-0.048    │           │\n",
      "│           │           │           │\n",
      "×───────────×           @───────────@^-0.022\n",
      "│           │           │           │\n",
      "│           │           ×───────────×\n",
      "│           │           │           │\n",
      "│           @───────────@^-0.033    │\n",
      "│           │           │           │\n",
      "│           ×───────────×           │\n",
      "│           │           │           │\n",
      "@───────────@^-0.033    @───────────@^-0.033\n",
      "│           │           │           │\n",
      "×───────────×           ×───────────×\n",
      "│           │           │           │\n",
      "Rz(-0.011π) @───────────@^-0.033    Rz(-0.024π)\n",
      "│           │           │           │\n",
      "│           ×───────────×           │\n",
      "│           │           │           │\n",
      "│           Rz(-0.011π) Rz(-0.024π) │\n",
      "│           │           │           │\n",
      "│           Rz(π)       Rz(π)       │\n",
      "│           │           │           │\n",
      "│           #2──────────YXXY^-1     │\n",
      "│           │           │           │\n",
      "│           │           #2──────────YXXY^-0.95\n",
      "│           │           │           │\n",
      "#2──────────YXXY^0.95   │           │\n",
      "│           │           │           │\n",
      "│           #2──────────YXXY^-1     │\n",
      "│           │           │           │\n",
      "│           │           @───────────@^-0.014\n",
      "│           │           │           │\n",
      "@───────────@^-0.006    ×───────────×\n",
      "│           │           │           │\n",
      "×───────────×           │           │\n",
      "│           │           │           │\n",
      "│           @───────────@^0.009     │\n",
      "│           │           │           │\n",
      "│           ×───────────×           │\n",
      "│           │           │           │\n",
      "@───────────@^0.009     @───────────@^0.009\n",
      "│           │           │           │\n",
      "×───────────×           ×───────────×\n",
      "│           │           │           │\n",
      "Rz(-0.007π) @───────────@^0.009     Rz(-0.003π)\n",
      "│           │           │           │\n",
      "Rz(π)       ×───────────×           Rz(π)\n",
      "│           │           │           │\n",
      "│           Rz(-0.007π) Rz(-0.003π) │\n",
      "│           │           │           │\n",
      "│           │           │           │\n",
      "│           │           │           │\n",
      "│           YXXY────────#2^-1       │\n",
      "│           │           │           │\n",
      "YXXY────────#2^-0.502   │           │\n",
      "│           │           │           │\n",
      "│           │           YXXY────────#2^0.502\n",
      "│           │           │           │\n",
      "│           YXXY────────#2^-1       │\n",
      "│           │           │           │\n",
      "@───────────@^0         │           │\n",
      "│           │           │           │\n",
      "×───────────×           @───────────@^0\n",
      "│           │           │           │\n",
      "│           │           ×───────────×\n",
      "│           │           │           │\n",
      "│           @───────────@^0         │\n",
      "│           │           │           │\n",
      "│           ×───────────×           │\n",
      "│           │           │           │\n",
      "@───────────@^0         @───────────@^0\n",
      "│           │           │           │\n",
      "×───────────×           ×───────────×\n",
      "│           │           │           │\n",
      "Rz(-0.0π)   @───────────@^0         Rz(-0.0π)\n",
      "│           │           │           │\n",
      "Rz(π)       ×───────────×           Rz(π)\n",
      "│           │           │           │\n",
      "│           Rz(-0.0π)   Rz(-0.0π)   │\n",
      "│           │           │           │\n",
      "│           │           │           │\n",
      "│           │           │           │\n",
      "│           #2──────────YXXY^-1     │\n",
      "│           │           │           │\n",
      "│           │           #2──────────YXXY^(-5/12)\n",
      "│           │           │           │\n",
      "#2──────────YXXY^(5/12) │           Rz(π)\n",
      "│           │           │           │\n",
      "│           #2──────────YXXY^-1     │\n",
      "│           │           │           │\n",
      "Rz(π)       │           Rz(π)       │\n",
      "│           │           │           │\n",
      "│           Rz(π)       │           │\n",
      "│           │           │           │\n",
      "│           #2──────────YXXY^-1     │\n",
      "│           │           │           │\n",
      "│           │           #2──────────YXXY^0.081\n",
      "│           │           │           │\n",
      "#2──────────YXXY^-0.081 │           Rz(0.112π)\n",
      "│           │           │           │\n",
      "│           #2──────────YXXY^-1     │\n",
      "│           │           │           │\n",
      "Rz(0.056π)  │           Rz(0.112π)  │\n",
      "│           │           │           │\n",
      "│           Rz(0.056π)  │           │\n",
      "│           │           │           │\n",
      "│           │           │           │\n",
      "│           │           │           │\n",
      "│           #2──────────YXXY^-1     │\n",
      "│           │           │           │\n",
      "│           │           #2──────────YXXY^-0.051\n",
      "│           │           │           │\n",
      "#2──────────YXXY^0.051  │           │\n",
      "│           │           │           │\n",
      "│           #2──────────YXXY^-1     │\n",
      "│           │           │           │\n",
      "│           │           @───────────@^-0.048\n",
      "│           │           │           │\n",
      "@───────────@^-0.022    ×───────────×\n",
      "│           │           │           │\n",
      "×───────────×           │           │\n",
      "│           │           │           │\n",
      "│           @───────────@^-0.033    │\n",
      "│           │           │           │\n",
      "│           ×───────────×           │\n",
      "│           │           │           │\n",
      "@───────────@^-0.033    @───────────@^-0.033\n",
      "│           │           │           │\n",
      "×───────────×           ×───────────×\n",
      "│           │           │           │\n",
      "Rz(-0.024π) @───────────@^-0.033    Rz(-0.011π)\n",
      "│           │           │           │\n",
      "│           ×───────────×           │\n",
      "│           │           │           │\n",
      "│           Rz(-0.024π) Rz(-0.011π) │\n",
      "│           │           │           │\n",
      "│           Rz(π)       Rz(π)       │\n",
      "│           │           │           │\n",
      "│           YXXY────────#2^-1       │\n",
      "│           │           │           │\n",
      "YXXY────────#2^-0.95    │           │\n",
      "│           │           │           │\n",
      "│           │           YXXY────────#2^0.95\n",
      "│           │           │           │\n",
      "│           YXXY────────#2^-1       │\n",
      "│           │           │           │\n",
      "@───────────@^-0.014    │           │\n",
      "│           │           │           │\n",
      "×───────────×           @───────────@^-0.006\n",
      "│           │           │           │\n",
      "│           │           ×───────────×\n",
      "│           │           │           │\n",
      "│           @───────────@^0.009     │\n",
      "│           │           │           │\n",
      "│           ×───────────×           │\n",
      "│           │           │           │\n",
      "@───────────@^0.009     @───────────@^0.009\n",
      "│           │           │           │\n",
      "×───────────×           ×───────────×\n",
      "│           │           │           │\n",
      "Rz(-0.003π) @───────────@^0.009     Rz(-0.007π)\n",
      "│           │           │           │\n",
      "Rz(π)       ×───────────×           Rz(π)\n",
      "│           │           │           │\n",
      "│           Rz(-0.003π) Rz(-0.007π) │\n",
      "│           │           │           │\n",
      "│           │           │           │\n",
      "│           │           │           │\n",
      "│           #2──────────YXXY^-1     │\n",
      "│           │           │           │\n",
      "│           │           #2──────────YXXY^-0.502\n",
      "│           │           │           │\n",
      "#2──────────YXXY^0.502  │           │\n",
      "│           │           │           │\n",
      "│           #2──────────YXXY^-1     │\n",
      "│           │           │           │\n",
      "│           │           @───────────@^0\n",
      "│           │           │           │\n",
      "@───────────@^0         ×───────────×\n",
      "│           │           │           │\n",
      "×───────────×           │           │\n",
      "│           │           │           │\n",
      "│           @───────────@^0         │\n",
      "│           │           │           │\n",
      "│           ×───────────×           │\n",
      "│           │           │           │\n",
      "@───────────@^0         @───────────@^0\n",
      "│           │           │           │\n",
      "×───────────×           ×───────────×\n",
      "│           │           │           │\n",
      "Rz(-0.0π)   @───────────@^0         Rz(-0.0π)\n",
      "│           │           │           │\n",
      "Rz(π)       ×───────────×           Rz(π)\n",
      "│           │           │           │\n",
      "│           Rz(-0.0π)   Rz(-0.0π)   │\n",
      "│           │           │           │\n",
      "│           │           │           │\n",
      "│           │           │           │\n",
      "│           YXXY────────#2^-1       │\n",
      "│           │           │           │\n",
      "YXXY────────#2^(-5/12)  │           │\n",
      "│           │           │           │\n",
      "Rz(π)       │           YXXY────────#2^(5/12)\n",
      "│           │           │           │\n",
      "│           YXXY────────#2^-1       │\n",
      "│           │           │           │\n",
      "│           Rz(π)       │           Rz(π)\n",
      "│           │           │           │\n",
      "│           │           Rz(π)       │\n",
      "│           │           │           │\n",
      "│           YXXY────────#2^-1       │\n",
      "│           │           │           │\n",
      "YXXY────────#2^0.081    │           │\n",
      "│           │           │           │\n",
      "Rz(0.112π)  │           YXXY────────#2^-0.081\n",
      "│           │           │           │\n",
      "│           YXXY────────#2^-1       │\n",
      "│           │           │           │\n",
      "│           Rz(0.112π)  │           Rz(0.056π)\n",
      "│           │           │           │\n",
      "│           │           Rz(0.056π)  │\n",
      "│           │           │           │\n",
      "│           │           │           │\n",
      "│           │           │           │\n",
      "│           YXXY────────#2^-1       │\n",
      "│           │           │           │\n",
      "YXXY────────#2^-0.051   │           │\n",
      "│           │           │           │\n",
      "│           │           YXXY────────#2^0.051\n",
      "│           │           │           │\n",
      "│           YXXY────────#2^-1       │\n",
      "│           │           │           │\n",
      "@───────────@^-0.048    │           │\n",
      "│           │           │           │\n",
      "×───────────×           @───────────@^-0.022\n",
      "│           │           │           │\n",
      "│           │           ×───────────×\n",
      "│           │           │           │\n",
      "│           @───────────@^-0.033    │\n",
      "│           │           │           │\n",
      "│           ×───────────×           │\n",
      "│           │           │           │\n",
      "@───────────@^-0.033    @───────────@^-0.033\n",
      "│           │           │           │\n",
      "×───────────×           ×───────────×\n",
      "│           │           │           │\n",
      "Rz(-0.011π) @───────────@^-0.033    Rz(-0.024π)\n",
      "│           │           │           │\n",
      "│           ×───────────×           │\n",
      "│           │           │           │\n",
      "│           Rz(-0.011π) Rz(-0.024π) │\n",
      "│           │           │           │\n",
      "│           Rz(π)       Rz(π)       │\n",
      "│           │           │           │\n",
      "│           #2──────────YXXY^-1     │\n",
      "│           │           │           │\n",
      "│           │           #2──────────YXXY^-0.95\n",
      "│           │           │           │\n",
      "#2──────────YXXY^0.95   │           │\n",
      "│           │           │           │\n",
      "│           #2──────────YXXY^-1     │\n",
      "│           │           │           │\n",
      "│           │           @───────────@^-0.014\n",
      "│           │           │           │\n",
      "@───────────@^-0.006    ×───────────×\n",
      "│           │           │           │\n",
      "×───────────×           │           │\n",
      "│           │           │           │\n",
      "│           @───────────@^0.009     │\n",
      "│           │           │           │\n",
      "│           ×───────────×           │\n",
      "│           │           │           │\n",
      "@───────────@^0.009     @───────────@^0.009\n",
      "│           │           │           │\n",
      "×───────────×           ×───────────×\n",
      "│           │           │           │\n",
      "Rz(-0.007π) @───────────@^0.009     Rz(-0.003π)\n",
      "│           │           │           │\n",
      "Rz(π)       ×───────────×           Rz(π)\n",
      "│           │           │           │\n",
      "│           Rz(-0.007π) Rz(-0.003π) │\n",
      "│           │           │           │\n",
      "│           │           │           │\n",
      "│           │           │           │\n",
      "│           YXXY────────#2^-1       │\n",
      "│           │           │           │\n",
      "YXXY────────#2^-0.502   │           │\n",
      "│           │           │           │\n",
      "│           │           YXXY────────#2^0.502\n",
      "│           │           │           │\n",
      "│           YXXY────────#2^-1       │\n",
      "│           │           │           │\n",
      "@───────────@^0         │           │\n",
      "│           │           │           │\n",
      "×───────────×           @───────────@^0\n",
      "│           │           │           │\n",
      "│           │           ×───────────×\n",
      "│           │           │           │\n",
      "│           @───────────@^0         │\n",
      "│           │           │           │\n",
      "│           ×───────────×           │\n",
      "│           │           │           │\n",
      "@───────────@^0         @───────────@^0\n",
      "│           │           │           │\n",
      "×───────────×           ×───────────×\n",
      "│           │           │           │\n",
      "Rz(-0.0π)   @───────────@^0         Rz(-0.0π)\n",
      "│           │           │           │\n",
      "Rz(π)       ×───────────×           Rz(π)\n",
      "│           │           │           │\n",
      "│           Rz(-0.0π)   Rz(-0.0π)   │\n",
      "│           │           │           │\n",
      "│           │           │           │\n",
      "│           │           │           │\n",
      "│           #2──────────YXXY^-1     │\n",
      "│           │           │           │\n",
      "│           │           #2──────────YXXY^(-5/12)\n",
      "│           │           │           │\n",
      "#2──────────YXXY^(5/12) │           │\n",
      "│           │           │           │\n",
      "│           #2──────────YXXY^-1     │\n",
      "│           │           │           │\n",
      "│           │           ×───────────×\n",
      "│           │           │           │\n",
      "×───────────×           │           │\n",
      "│           │           │           │\n",
      "│           ×───────────×           │\n",
      "│           │           │           │\n",
      "×───────────×           ×───────────×\n",
      "│           │           │           │\n",
      "│           ×───────────×           │\n",
      "│           │           │           │\n"
     ]
    }
   ],
   "source": [
    "# Initialize a random initial state.\n",
    "import numpy\n",
    "random_seed = 8317\n",
    "initial_state = openfermion.haar_random_vector(\n",
    "    2 ** n_qubits, random_seed).astype(numpy.complex64)\n",
    "\n",
    "# Numerically compute the correct circuit output.\n",
    "import scipy\n",
    "hamiltonian_sparse = openfermion.get_sparse_operator(molecular_hamiltonian)\n",
    "exact_state = scipy.sparse.linalg.expm_multiply(\n",
    "    -1j * time * hamiltonian_sparse, initial_state)\n",
    "\n",
    "# Trotter step paramaters.\n",
    "n_steps = 3\n",
    "\n",
    "# Compile the low rank Trotter step using OpenFermion-Cirq.\n",
    "qubits = cirq.LineQubit.range(n_qubits)\n",
    "circuit = cirq.Circuit(\n",
    "    trotter.simulate_trotter(\n",
    "            qubits, molecular_hamiltonian,\n",
    "            time=time, n_steps=n_steps,\n",
    "            algorithm=trotter.LOW_RANK),\n",
    "    strategy=cirq.InsertStrategy.EARLIEST)\n",
    "\n",
    "# Use Cirq simulator to apply circuit.\n",
    "simulator = cirq.Simulator()\n",
    "result = simulator.simulate(circuit, qubit_order=qubits, initial_state=initial_state)\n",
    "simulated_state = result.final_state\n",
    "\n",
    "# Print final fidelity.\n",
    "fidelity = abs(numpy.dot(simulated_state, numpy.conjugate(exact_state))) ** 2\n",
    "print('Fidelity with exact result is {}.\\n'.format(fidelity))\n",
    "\n",
    "# Print circuit.\n",
    "cirq.DropNegligible().optimize_circuit(circuit)\n",
    "print(circuit.to_text_diagram(transpose=True))"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
